Cargamos los paquetes necesarios

library(dplyr)
library(sf)
library(gstat)
library(geoR)
library(spdep)
library(DataExplorer)
library(psych)
library(ggcorrplot)
library(biogeo)
library(ggplot2)
library(leaflet)

Cargamos los datasets

estaciones <- read.csv("data_smn/preprocessed/estaciones_smn_v2.csv")
horarios <- read.csv("data_smn/preprocessed/datohorario20210420.csv")

Observamos como esta compuesto el dataset

glimpse(estaciones)
Rows: 123
Columns: 9
$ NOMBRE           <chr> "BASE BELGRANO II", "BASE CARLINI (EX JUBANY)", "BASE ESPERANZA", "BASE MARAMBIO", "BASE O…
$ PROVINCIA        <chr> "ANTARTIDA", "ANTARTIDA", "ANTARTIDA", "ANTARTIDA", "ANTARTIDA", "ANTARTIDA", "BUENOS AIRE…
$ LATITUD_GRADOS   <int> 77, 62, 63, 64, 60, 68, 36, 38, 37, 36, 34, 38, 37, 36, 34, 34, 34, 34, 36, 37, 34, 34, 34…
$ LATITUD_MINUTOS  <int> 52, 14, 24, 14, 45, 8, 50, 44, 43, 12, 32, 1, 26, 21, 36, 49, 33, 58, 2, 56, 33, 41, 40, 2…
$ LONGITUD_GRADOS  <int> 34, 58, 56, 56, 44, 67, 59, 62, 59, 61, 58, 61, 61, 57, 58, 58, 60, 57, 59, 57, 58, 58, 58…
$ LONGITUD_MINUTOS <int> 34, 38, 59, 43, 43, 8, 53, 10, 47, 4, 40, 20, 53, 44, 36, 32, 55, 54, 8, 35, 49, 44, 38, 5…
$ ALTURA           <int> 256, 11, 24, 198, 12, 7, 147, 83, 207, 94, 26, 247, 233, 9, 12, 20, 81, 23, 36, 21, 32, 36…
$ NRO              <int> 89034, 89053, 88963, 89055, 88968, 89066, 87641, 87750, 87649, 87640, 87570, 87683, 87637,…
$ NroOACI          <chr> "SAYB", "SAYJ", "SAYE", "SAWB", "SAYO", "SAYS", "SAZA", "SAZB", "SAZJ", "SAZI", "SADO", "S…
print("#--------------------#")
[1] "#--------------------#"
glimpse(horarios)
Rows: 2,041
Columns: 8
$ FECHA  <int> 20042021, 20042021, 20042021, 20042021, 20042021, 20042021, 20042021, 20042021, 20042021, 20042021, …
$ HORA   <int> 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 0, 1, 2, 3, 4,…
$ TEMP   <dbl> 20.7, 19.8, 19.5, 19.2, 19.2, 18.9, 19.0, 18.8, 19.0, 19.1, 19.9, 20.3, 21.8, 22.9, 23.2, 23.7, 23.2…
$ HUM    <int> 70, 79, 79, 81, 81, 84, 84, 84, 84, 84, 81, 82, 68, 72, 68, 66, 71, 68, 75, 72, 73, 68, 68, 68, 90, …
$ PNM    <dbl> 1020.8, 1020.9, 1020.9, 1020.4, 1020.0, 1020.3, 1020.7, 1021.1, 1021.6, 1022.2, 1022.4, 1021.9, 1021…
$ DD     <int> 70, 70, 70, 70, 70, 50, 70, 50, 50, 50, 50, 50, 50, 70, 70, 70, 70, 70, 90, 90, 90, 90, 70, 70, 50, …
$ FF     <int> 28, 28, 26, 26, 22, 22, 17, 17, 15, 19, 20, 24, 20, 19, 20, 17, 15, 15, 19, 20, 22, 22, 31, 35, 17, …
$ NOMBRE <chr> "AEROPARQUE AERO", "AEROPARQUE AERO", "AEROPARQUE AERO", "AEROPARQUE AERO", "AEROPARQUE AERO", "AERO…

Observamos que la variable que representa la velocidad del viento en km/h (FF) es de tipo Char, por lo cual debemos convertila en int.

horarios$FF <- as.numeric(horarios$FF)

Observamos el resumen estadistico de las variables de potencial interes

print("altura")
[1] "altura"
summary(estaciones$ALTURA)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    3.0    53.5   145.0   331.5   455.0  3459.0 
print("hora")
[1] "hora"
summary(horarios$HORA)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   0.00    7.00   12.00   12.16   18.00   23.00 
print("temperatura")
[1] "temperatura"
summary(horarios$TEMP)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
 -20.00   16.10   20.20   19.09   23.80   33.60       1 
print("humedad")
[1] "humedad"
summary(horarios$HUM)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
  18.00   59.00   72.00   71.18   85.00  100.00       4 
print("presion atmosferica")
[1] "presion atmosferica"
summary(horarios$PNM)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
  960.8  1011.0  1015.1  1075.3  1019.2  3133.0      23 
print("direccion del viento")
[1] "direccion del viento"
summary(horarios$DD)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
    0.0    50.0    70.0   118.9   160.0   990.0       1 
print("velocidad del viento")
[1] "velocidad del viento"
summary(horarios$FF)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
   0.00    9.00   15.00   16.06   22.00  107.00       1 

Se puede observar la presencia de observaciones NA’s, por lo cual procedemos a eliminarlas

which(is.na(horarios), arr.ind=TRUE)
       row col
 [1,]  771   3
 [2,]  587   4
 [3,]  771   4
 [4,] 1876   4
 [5,] 1877   4
 [6,]  237   5
 [7,]  771   5
 [8,]  973   5
 [9,]  974   5
[10,]  975   5
[11,]  976   5
[12,]  977   5
[13,]  978   5
[14,]  979   5
[15,]  980   5
[16,] 1392   5
[17,] 1393   5
[18,] 1394   5
[19,] 1395   5
[20,] 1396   5
[21,] 1397   5
[22,] 1398   5
[23,] 1399   5
[24,] 1400   5
[25,] 1401   5
[26,] 1402   5
[27,] 1403   5
[28,] 1441   5
[29,]  771   6
[30,]  771   7
horarios[771,]

Como toda la fila para la rioja aero es NA, lla eliminamos

horarios <- horarios[-c(771), ]  
rownames(horarios) <- 1:nrow(horarios)
horarios[587,]

Completamos la columna humedad con el promedio

# FORMOSA AERO
formosa <- horarios[horarios$NOMBRE == "FORMOSA AERO",]
which(is.na(formosa), arr.ind=TRUE)
    row col
587   4   4
formosa <- na.omit(formosa)
horarios[587,4] = mean(formosa$HUM)
horarios[587,]

remove(formosa)
which(is.na(horarios), arr.ind=TRUE)
      row col
1875 1875   4
1876 1876   4
237   237   5
972   972   5
973   973   5
974   974   5
975   975   5
976   976   5
977   977   5
978   978   5
979   979   5
1391 1391   5
1392 1392   5
1393 1393   5
1394 1394   5
1395 1395   5
1396 1396   5
1397 1397   5
1398 1398   5
1399 1399   5
1400 1400   5
1401 1401   5
1402 1402   5
1440 1440   5
horarios[c(1875,1876),]

Completamos con el promedio de humedad calculado para tucuman aero

# TUCUMAN AERO
tucuman_aero <- horarios[horarios$NOMBRE == "TUCUMAN AERO",]
which(is.na(tucuman_aero), arr.ind=TRUE)
     row col
1875   1   4
1876   2   4

tucuman_aero <- na.omit(tucuman_aero)
horarios[1875,4] = mean(tucuman_aero$HUM)
horarios[1876,4] = mean(tucuman_aero$HUM)
horarios[c(1875,1876),]

remove(tucuman_aero)
horarios[237,]
# CATAMARCA AERO
catamarca_aero <- horarios[horarios$NOMBRE == "CATAMARCA AERO",]
which(is.na(catamarca_aero), arr.ind=TRUE)
    row col
237   2   5
catamarca_aero <- na.omit(catamarca_aero)
horarios[237,5] = mean(catamarca_aero$PNM)
horarios[237,]
remove(catamarca_aero)
horarios[c(972,973,974,975,976,977,978,979),]
# MERCEDES AERO
mercedes_aero <- horarios[horarios$NOMBRE == "MERCEDES AERO",]
which(is.na(mercedes_aero), arr.ind=TRUE)
    row col
972   1   5
973   2   5
974   3   5
975   4   5
976   5   5
977   6   5
978   7   5
979   8   5
remove(mercedes_aero)

Vamos a completar los valores faltantes para la presion atmosferica con el promedio de la provincia de corrientes a la que pertenece Mercedes aero

corrientes_aero <- horarios[(horarios$NOMBRE == "CORRIENTES AERO") | (horarios$NOMBRE == "ITUZAINGO") | (horarios$NOMBRE == "MONTE CASEROS AERO") | (horarios$NOMBRE == "PASO DE LOS LIBRES AERO"),]
which(is.na(corrientes_aero), arr.ind=TRUE)
     row col
horarios[972,5] = mean(corrientes_aero$PNM)
horarios[973,5] = mean(corrientes_aero$PNM)
horarios[974,5] = mean(corrientes_aero$PNM)
horarios[975,5] = mean(corrientes_aero$PNM)
horarios[976,5] = mean(corrientes_aero$PNM)
horarios[977,5] = mean(corrientes_aero$PNM)
horarios[978,5] = mean(corrientes_aero$PNM)
horarios[979,5] = mean(corrientes_aero$PNM)

horarios[c(972,973,974,975,976,977,978,979),]
remove(corrientes_aero)
horarios[c(1391,1392,1394,1395,1396,1397,1398,1399,1400,1401,1402,1403),]
# RIO GALLEGOS AERO
gallegos_aero <- horarios[horarios$NOMBRE == "RIO GALLEGOS AERO",]
which(is.na(gallegos_aero), arr.ind=TRUE)
     row col
1391   1   5
1392   2   5
1393   3   5
1394   4   5
1395   5   5
1396   6   5
1397   7   5
1398   8   5
1399   9   5
1400  10   5
1401  11   5
1402  12   5
gallegos_aero <- na.omit(gallegos_aero)
horarios[1391,5] = mean(gallegos_aero$PNM)
horarios[1392,5] = mean(gallegos_aero$PNM)
horarios[1393,5] = mean(gallegos_aero$PNM)
horarios[1394,5] = mean(gallegos_aero$PNM)
horarios[1395,5] = mean(gallegos_aero$PNM)
horarios[1396,5] = mean(gallegos_aero$PNM)
horarios[1397,5] = mean(gallegos_aero$PNM)
horarios[1398,5] = mean(gallegos_aero$PNM)
horarios[1399,5] = mean(gallegos_aero$PNM)
horarios[1400,5] = mean(gallegos_aero$PNM)
horarios[1401,5] = mean(gallegos_aero$PNM)
horarios[1402,5] = mean(gallegos_aero$PNM)
horarios[1403,5] = mean(gallegos_aero$PNM)
horarios[c(1391,1392,1394,1395,1396,1397,1398,1399,1400,1401,1402,1403),]
remove(gallegos_aero)
which(is.na(horarios), arr.ind=TRUE)
      row col
1440 1440   5
horarios[1440,]
# RIO GRANDE
rio_grande_aero <- horarios[horarios$NOMBRE == "RIO GRANDE B.A.",]
which(is.na(rio_grande_aero), arr.ind=TRUE)
     row col
1440  26   5
rio_grande_aero <- na.omit(rio_grande_aero)
horarios[1440,5] = mean(rio_grande_aero$PNM)
horarios[1440,]

remove(rio_grande_aero)

Validamos que efectivamente se hayan arreglado

print("temperatura")
[1] "temperatura"
summary(horarios$TEMP)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
 -20.00   16.10   20.20   19.09   23.80   33.60 
print("humedad")
[1] "humedad"
summary(horarios$HUM)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  18.00   59.00   72.00   71.19   85.00  100.00 
print("presion atmosferica")
[1] "presion atmosferica"
summary(horarios$PNM)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  960.8  1011.1  1015.1  1074.7  1019.1  3133.0 
print("direccion del viento")
[1] "direccion del viento"
summary(horarios$DD)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    0.0    50.0    70.0   118.9   160.0   990.0 
print("velocidad del viento")
[1] "velocidad del viento"
summary(horarios$FF)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   0.00    9.00   15.00   16.06   22.00  107.00 

Perfecto, ya no tenemos valores nulos en nuestro dataset.

Analisis descriptivo de las variables

A continuación, visualizaremos las distribuciones de las variables numericas de interés

hist(estaciones$ALTURA, main = "Histograma de la altura", xlab = "Altura")

hist(horarios$HORA, main = "Histograma de horarios", xlab = "Horarios")

hist(horarios$TEMP, main = "Histograma de temperatura", xlab = "Temperatura")

hist(horarios$HUM, main = "Histograma de humedad", xlab = "Humedad")

hist(horarios$PNM, main = "Histograma de presion atmosferica", xlab = "Presion Atmosferica")

hist(horarios$DD, main = "Histograma de la direccion del viento", xlab = "Direccion del viento")

hist(horarios$FF, main = "Histograma de la velocidad del viento", xlab = "Velocidad del viento")

Creamos unos boxplot para visualizar la distribucionde datos que potencialmente nos interecen para proceder con el analisis

boxplot(estaciones$ALTURA, main = "Boxplot de la altura", xlab = "Altura")

boxplot(horarios$HORA, main = "Boxplot de horarios", xlab = "Horarios")

boxplot(horarios$TEMP, main = "Boxplot de temperatura", xlab = "Temperatura")

boxplot(horarios$HUM, main = "Boxplot de humedad", xlab = "Humedad")

boxplot(horarios$PNM, main = "Boxplot de presion atmosferica", xlab = "Presion Atmosferica")

boxplot(horarios$DD, main = "Boxplot de la direccion del viento", xlab = "Direccion del viento")

boxplot(horarios$FF, main = "Boxplot de la velocidad del viento", xlab = "Velocidad del viento")

Los boxplots anteriores ponen en evidencia la existencia de outliers. ¿Pero son estos realmente outliers, o pertenecen a observaciones en lugares muy remotos? Esto lo analizaremos luego, al momento de graficar las estaciones en el mapa de Argentina.

Ahora, veamos que tan correlacionadas estan estas variables.

corr <- cor(horarios[, c(2,3,4,5,6,7)], use = "complete.obs")

ggcorrplot(corr, type = "lower", outline.col = "black",
 lab=TRUE,
 ggtheme = ggplot2::theme_gray,
 colors = c("#6D9EC1", "white", "#E46726"))

Las variables que mas correlacionan con la velocidad del viento son HUMEDAD (negativamente) y HORA (positivamente). Tambien vemos que HORA y TEMPERATURA correlacionan negativamentecon HUMEDAD. Por ultimo, se observa que HORA y TEMPERATURA correlacionan positivamente

Analizamos ahora la simetria de la variable que representa la velocidad del viento ya que es la que mas nos interesa en este estudio.

skew(horarios$FF)
[1] 1.588087
kurtosi(horarios$FF)
[1] 7.188948

La medida de asimetria y kurtosi terminan de validar lo que observamos en el histograma. La variable FF es asimetrica a derecha y tiene una mayor concentracion de valores muy cerca de la media de la distribución y muy lejos de la cola de la distribucion.

Preprocesamiento de dataset

Un detalle no menor del dataset de estaciones es que las latitudes y longitudes estan expresadads en grados y minutos. Para poder trabajar con ellas, necesitamos que esten expresadas en valores decimales. Por eso, en el siguiente bloque de código vamos a usar la funcion dms2dd para hacer esta conversión.

# creamos dos vectores vacios
latitud <- c()
longitud <- c()

# iteramos por cada fila del dataset de estaciones y hacemos la convesion de latitud y longitud
for(i in 1:nrow(estaciones)) {
     latitud[i] <- dms2dd(dd = estaciones[i, "LATITUD_GRADOS"], mm = estaciones[i, "LATITUD_MINUTOS"], ss = 0, ns = "S")
     longitud[i] <- dms2dd(dd = estaciones[i, "LONGITUD_GRADOS"], mm = estaciones[i, "LONGITUD_MINUTOS"], ss = 0, ns = "S")
}

# asignamos a latitud y longitud los valores convertidos
estaciones['LATITUD'] <- latitud
estaciones['LONGITUD'] <- longitud

Antes de unir estaciones, se removeran las columnas que no sean relevantes para este analisis. Las mismas son NRO y NroOACI, LATITUD_GRADOS, LATITUD_MINUTOS, LONGITUD_GRADOS, LONGITUD_MINUTOS. Asi como tambien se removera la variable fecha, ya que estos datos pertenecen al 20/04/2021

estaciones <- estaciones[c(1,2,7,10,11)]
horarios <- horarios[,c(3,4,5,6,7,8)]
summary(estaciones)
    NOMBRE           PROVINCIA             ALTURA          LATITUD          LONGITUD     
 Length:123         Length:123         Min.   :   3.0   Min.   :-77.87   Min.   :-72.05  
 Class :character   Class :character   1st Qu.:  53.5   1st Qu.:-38.17   1st Qu.:-66.47  
 Mode  :character   Mode  :character   Median : 145.0   Median :-34.53   Median :-63.37  
                                       Mean   : 331.5   Mean   :-35.96   Mean   :-62.82  
                                       3rd Qu.: 455.0   3rd Qu.:-30.25   3rd Qu.:-58.93  
                                       Max.   :3459.0   Max.   :-22.10   Max.   :-34.57  
summary(horarios$TEMP)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
 -20.00   16.10   20.20   19.09   23.80   33.60 

Se procede a unificar las dos tablas usando la variable NOMBRE como punto para combinar los datasets

data <- inner_join(estaciones, horarios, by = c("NOMBRE" = "NOMBRE"))

glimpse(data)
Rows: 2,040
Columns: 10
$ NOMBRE    <chr> "BASE BELGRANO II", "BASE BELGRANO II", "BASE BELGRANO II", "BASE BELGRANO II", "BASE BELGRANO II…
$ PROVINCIA <chr> "ANTARTIDA", "ANTARTIDA", "ANTARTIDA", "ANTARTIDA", "ANTARTIDA", "ANTARTIDA", "ANTARTIDA", "ANTAR…
$ ALTURA    <int> 256, 256, 256, 256, 256, 256, 256, 256, 11, 11, 11, 11, 11, 11, 11, 11, 24, 24, 24, 24, 24, 24, 2…
$ LATITUD   <dbl> -77.86667, -77.86667, -77.86667, -77.86667, -77.86667, -77.86667, -77.86667, -77.86667, -62.23333…
$ LONGITUD  <dbl> -34.56667, -34.56667, -34.56667, -34.56667, -34.56667, -34.56667, -34.56667, -34.56667, -58.63333…
$ TEMP      <dbl> -20.0, -16.2, -16.9, -13.0, -15.1, -16.6, -15.3, -16.8, -0.1, -0.5, -0.9, -0.5, 0.3, 1.1, 1.3, 2.…
$ HUM       <dbl> 70, 72, 80, 73, 86, 66, 77, 85, 88, 82, 77, 75, 84, 92, 99, 91, 43, 49, 67, 81, 67, 62, 77, 83, 7…
$ PNM       <dbl> 975.7, 970.4, 966.7, 964.2, 961.4, 963.0, 964.8, 967.1, 992.9, 993.0, 992.9, 994.6, 995.3, 995.6,…
$ DD        <int> 140, 140, 110, 140, 160, 160, 90, 110, 230, 230, 270, 290, 290, 320, 320, 320, 160, 200, 50, 200,…
$ FF        <dbl> 13, 30, 50, 52, 74, 61, 44, 13, 15, 15, 26, 28, 41, 37, 50, 74, 20, 7, 13, 7, 11, 46, 56, 74, 19,…
summary(data$NOMBRE)
   Length     Class      Mode 
     2040 character character 
summary(data$PROVINCIA)
   Length     Class      Mode 
     2040 character character 
summary(data$ALTURA)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    3.0    43.0   125.0   309.2   450.0  3459.0 
summary(data$LATITUD)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
 -77.87  -37.63  -34.13  -35.47  -30.27  -22.10 
summary(data$LONGITUD)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
 -72.05  -66.28  -63.02  -62.85  -58.73  -34.57 
summary(data$TEMP)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
 -20.00   16.10   20.20   19.09   23.80   33.60 
summary(data$HUM)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  18.00   59.00   72.00   71.19   85.00  100.00 
summary(data$PNM)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  960.8  1011.1  1015.1  1074.7  1019.1  3133.0 
summary(data$DD)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    0.0    50.0    70.0   118.9   160.0   990.0 
summary(data$FF)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   0.00    9.00   15.00   16.06   22.00  107.00 

Volvemos realizar el grafico de correlacion incluyendo la variable altura.

corr <- cor(data[, c(3,4,5,6,7,8,9,10)], use = "complete.obs")

ggcorrplot(corr, type = "lower", outline.col = "black",
 lab=TRUE,
 ggtheme = ggplot2::theme_gray,
 colors = c("#6D9EC1", "white", "#E46726"))

TODO: AGREGAR CONCLUSION DEL GRAFICO DE CORRELACION

Se observa que hay estaciones que tiene las observaciones en 0 para la variable FF y DD. Consideramos esto como un error en el instrumento de medicion, por lo cual vamos a eliminar a esa estacion del analisis.

data = data[(data$FF != 0) & (data$DD != 0),]
rownames(data) <- 1:nrow(data)

Agrupamos los datos por nombre calculando el promedio y desvio del viento

data_agg = data %>%
  group_by(NOMBRE) %>%
  summarise(MEAN_VIENTO_KMH = mean(FF), 
            SD_VIENTO_KMH = sd(FF), 
            LONGITUD = unique(LONGITUD), 
            LATITUD = unique(LATITUD), 
            .groups = "keep")

Vemos que en el dataset resultante nos quedan 98 observaciones que coinciden con la cantidad de estaciones meteorologicas originales

Convertimos data_agg a data.frame ya que necesitamos este tipo de dato para poder trabajar

data_agg = data.frame(data_agg)

Transformamos df_data_agg en un archivo geográfico utilizando el código de proyección mercator

data_agg_sf = st_as_sf(data_agg, coords = c("LONGITUD", "LATITUD"), crs = 4326)

Validamos la clase del nuevo dataframe

class(data_agg_sf)
[1] "sf"         "data.frame"

Analisis exploratorio espacial

Grafico de las estaciones meteorologias

En el siguiente gráfico de la republica argentina se observan en color azul las estaciones meteorológicas donde se realizaron las mediciones de la variables que estan presentes en el dataset

Queremos que en el mapa se vea como etiqueta el nombre de la base meteorologica. Para eso aplicamos la siguiente funcion

labs <- lapply(seq(nrow(data_agg_sf)), function(i) {
  paste0( '<p>', data_agg_sf[i, "NOMBRE"], '<p>', '<p>',data_agg_sf[i, "MEAN_VIENTO_KMH"],'</p>' ) })

Realizamos el grafico interactivo de las estaciones meteorologicas graficadas sobre un mapa de Argentina.

leaflet() %>%
  addTiles() %>%
  addCircles(data = data_agg_sf, weight = 3, label = lapply(labs, htmltools::HTML))
NA

Limpieza de datos

En el mapa observamos que hay puntos muy distantes de la Argentina continental. Dado el proposito de este estudio, el cual es determinar la ubicacion geografica óptima en base a la variable velocidad del viento, decidimos remover estas observaciones ya que no aporta informacion util y ademas agregan ruido a nuestro analisis.

Primero, vamos a borrar las estaciones que no estan en la plataforma continental argentina - Base Carlini - Base San Martin - Base Marambio - Base Esperanza - Base Orcadas

data_agg_sf = data_agg_sf[data_agg_sf$NOMBRE != "BASE CARLINI (EX JUBANY)",]
data_agg_sf = data_agg_sf[data_agg_sf$NOMBRE != "BASE SAN MARTIN",]
data_agg_sf = data_agg_sf[data_agg_sf$NOMBRE != "BASE MARAMBIO",]
data_agg_sf = data_agg_sf[data_agg_sf$NOMBRE != "BASE ESPERANZA",]
data_agg_sf = data_agg_sf[data_agg_sf$NOMBRE != "BASE ORCADAS",]
data_agg_sf = data_agg_sf[data_agg_sf$NOMBRE != "BASE BELGRANO II",]

Repetimos el plot para validar

labs <- lapply(seq(nrow(data_agg_sf)), function(i) {
  paste0( '<p>', data_agg_sf[i, "NOMBRE"], '</p>' ) })


leaflet() %>%
  addTiles() %>%
  addCircles(data = data_agg_sf, weight = 3, label = lapply(labs, htmltools::HTML))
NA

Observemos el resumen estadistico de las nuevas variables MEAN_VIENTO_KMH y SD_VIENTO_KMH

describe(data_agg_sf$MEAN_VIENTO_KMH)
hist(data_agg_sf$MEAN_VIENTO_KMH)

boxplot(data_agg_sf$MEAN_VIENTO_KMH)

describe(data_agg_sf$SD_VIENTO_KMH)
hist(data_agg_sf$SD_VIENTO_KMH)

boxplot(data_agg_sf$SD_VIENTO_KMH)

Veamos si esta nueva variable MEAN_VIENTO_KMH es normal.

hist(data_agg_sf$MEAN_VIENTO_KMH)

boxplot(data_agg_sf$MEAN_VIENTO_KMH)

qqnorm(data_agg_sf$MEAN_VIENTO_KMH)
qqline(data_agg_sf$MEAN_VIENTO_KMH, col=2)

shapiro.test(data_agg_sf$MEAN_VIENTO_KMH)

    Shapiro-Wilk normality test

data:  data_agg_sf$MEAN_VIENTO_KMH
W = 0.94243, p-value = 9.648e-05

Claramente la variable MEAN_VIENTO_KMH no es normal. El qqplot pone en evidencia la existencia de colas pesadas. Ademas, al realizar el test de shapiro wilk el p-value obtenido es menor a 0.05, lo cual indica que los datos que tenemos no son normales #TODO: agregar algo mas a esta conclusion?

Ahora, procedemos a analizar la existencia de inliers, y en el caso de encontrarlos, eliminarlos.Para eso, usamos el test de moran. Basicamente lo que estamos testeando es que el promedio del viento este dristribuido de manera aleatoria siguiendo un proceso aleatorio.

knea <- knearneigh(data_agg_sf)
neib <- knn2nb(knea)

listw <- nb2listw(neib)

moran_test <- moran.test(data_agg_sf$MEAN_VIENTO_KMH, listw)
moran_test

    Moran I test under randomisation

data:  data_agg_sf$MEAN_VIENTO_KMH  
weights: listw    

Moran I statistic standard deviate = 3.8355, p-value = 6.265e-05
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.423564794      -0.008849558       0.012710177 
geary_test <- geary.test(data_agg_sf$MEAN_VIENTO_KMH, listw)
geary_test

    Geary C test under randomisation

data:  data_agg_sf$MEAN_VIENTO_KMH 
weights: listw 

Geary C statistic standard deviate = 4.083, p-value = 2.223e-05
alternative hypothesis: Expectation greater than statistic
sample estimates:
Geary C statistic       Expectation          Variance 
       0.46134647        1.00000000        0.01740455 
shaphiro_test <- shapiro.test(data_agg_sf$MEAN_VIENTO_KMH)
shaphiro_test

    Shapiro-Wilk normality test

data:  data_agg_sf$MEAN_VIENTO_KMH
W = 0.94243, p-value = 9.648e-05
moran <- moran.plot(data_agg_sf$MEAN_VIENTO_KMH, listw = listw)

data_agg_sf[10,]
Simple feature collection with 1 feature and 3 fields
Geometry type: POINT
Dimension:     XY
Bounding box:  xmin: -65.76667 ymin: -28.6 xmax: -65.76667 ymax: -28.6
Geodetic CRS:  WGS 84
           NOMBRE MEAN_VIENTO_KMH SD_VIENTO_KMH                geometry
16 CATAMARCA AERO            47.5      11.19949 POINT (-65.76667 -28.6)
data_agg_sf[31,]
Simple feature collection with 1 feature and 3 fields
Geometry type: POINT
Dimension:     XY
Bounding box:  xmin: -63.75 ymin: -35.7 xmax: -63.75 ymax: -35.7
Geodetic CRS:  WGS 84
              NOMBRE MEAN_VIENTO_KMH SD_VIENTO_KMH             geometry
37 GENERAL PICO AERO           30.25      4.234777 POINT (-63.75 -35.7)
data_agg_sf[79,]
Simple feature collection with 1 feature and 3 fields
Geometry type: POINT
Dimension:     XY
Bounding box:  xmin: -64.23333 ymin: -33.11667 xmax: -64.23333 ymax: -33.11667
Geodetic CRS:  WGS 84
            NOMBRE MEAN_VIENTO_KMH SD_VIENTO_KMH                    geometry
85 RIO CUARTO AERO            32.5      7.607014 POINT (-64.23333 -33.11667)
data_agg_sf[108,]
Simple feature collection with 1 feature and 3 fields
Geometry type: POINT
Dimension:     XY
Bounding box:  xmin: -61.96667 ymin: -33.66667 xmax: -61.96667 ymax: -33.66667
Geodetic CRS:  WGS 84
                NOMBRE MEAN_VIENTO_KMH SD_VIENTO_KMH                    geometry
114 VENADO TUERTO AERO              32      11.09955 POINT (-61.96667 -33.66667)
data_agg_sf[68,]
Simple feature collection with 1 feature and 3 fields
Geometry type: POINT
Dimension:     XY
Bounding box:  xmin: -62.38333 ymin: -37.6 xmax: -62.38333 ymax: -37.6
Geodetic CRS:  WGS 84
       NOMBRE MEAN_VIENTO_KMH SD_VIENTO_KMH                geometry
74 PIGUE AERO        38.23077      6.905962 POINT (-62.38333 -37.6)
data_agg_sf[21,]
Simple feature collection with 1 feature and 3 fields
Geometry type: POINT
Dimension:     XY
Bounding box:  xmin: -61.88333 ymin: -37.43333 xmax: -61.88333 ymax: -37.43333
Geodetic CRS:  WGS 84
                NOMBRE MEAN_VIENTO_KMH SD_VIENTO_KMH                    geometry
27 CORONEL SUAREZ AERO        27.33333      5.278889 POINT (-61.88333 -37.43333)
data_agg_sf[89,]
Simple feature collection with 1 feature and 3 fields
Geometry type: POINT
Dimension:     XY
Bounding box:  xmin: -66.35 ymin: -33.26667 xmax: -66.35 ymax: -33.26667
Geodetic CRS:  WGS 84
          NOMBRE MEAN_VIENTO_KMH SD_VIENTO_KMH                 geometry
95 SAN LUIS AERO              17      7.901568 POINT (-66.35 -33.26667)
data_agg_sf[23,]
Simple feature collection with 1 feature and 3 fields
Geometry type: POINT
Dimension:     XY
Bounding box:  xmin: -57.73333 ymin: -36.35 xmax: -57.73333 ymax: -36.35
Geodetic CRS:  WGS 84
         NOMBRE MEAN_VIENTO_KMH SD_VIENTO_KMH                 geometry
29 DOLORES AERO        15.83333      6.105355 POINT (-57.73333 -36.35)
data_agg_sf[109,]
Simple feature collection with 1 feature and 3 fields
Geometry type: POINT
Dimension:     XY
Bounding box:  xmin: -65.43333 ymin: -36.21667 xmax: -65.43333 ymax: -36.21667
Geodetic CRS:  WGS 84
       NOMBRE MEAN_VIENTO_KMH SD_VIENTO_KMH                    geometry
115 VICTORICA              14      8.660254 POINT (-65.43333 -36.21667)
data_agg_sf = data_agg_sf[data_agg_sf$NOMBRE != "CATAMARCA AERO",]
data_agg_sf = data_agg_sf[data_agg_sf$NOMBRE != "GENERAL PICO AERO",]
data_agg_sf = data_agg_sf[data_agg_sf$NOMBRE != "RIO CUARTO AERO",]
data_agg_sf = data_agg_sf[data_agg_sf$NOMBRE != "VENADO TUERTO AERO",]
data_agg_sf = data_agg_sf[data_agg_sf$NOMBRE != "PIGUE AERO",]
data_agg_sf = data_agg_sf[data_agg_sf$NOMBRE != "CORONEL SUAREZ AERO",]
data_agg_sf = data_agg_sf[data_agg_sf$NOMBRE != "SAN LUIS AERO",]
data_agg_sf = data_agg_sf[data_agg_sf$NOMBRE != "DOLORES AERO",]
data_agg_sf = data_agg_sf[data_agg_sf$NOMBRE != "VICTORICA",]
rownames(data_agg_sf) <- 1:nrow(data_agg_sf)

Inliers: verificamos los resultados dsp de eliminar inliers

# Creamos una lista de vecinos
knea <- knearneigh(data_agg_sf)
neib <- knn2nb(knea)
listw <- nb2listw(neib)

# Hacemos el test de moran 
moran_test_v2 <- moran.test(data_agg_sf$MEAN_VIENTO_KMH, listw)
moran_test_v2

    Moran I test under randomisation

data:  data_agg_sf$MEAN_VIENTO_KMH  
weights: listw    

Moran I statistic standard deviate = 4.1509, p-value = 1.656e-05
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.483041908      -0.009615385       0.014086783 
geary_test_v2 <- geary.test(data_agg_sf$MEAN_VIENTO_KMH, listw)
geary_test_v2

    Geary C test under randomisation

data:  data_agg_sf$MEAN_VIENTO_KMH 
weights: listw 

Geary C statistic standard deviate = 4.6029, p-value = 2.083e-06
alternative hypothesis: Expectation greater than statistic
sample estimates:
Geary C statistic       Expectation          Variance 
       0.41937891        1.00000000        0.01591176 
shaphiro_test_v2 <- shapiro.test(data_agg_sf$MEAN_VIENTO_KMH)
shaphiro_test_v2

    Shapiro-Wilk normality test

data:  data_agg_sf$MEAN_VIENTO_KMH
W = 0.96597, p-value = 0.008459
moran.plot(data_agg_sf$MEAN_VIENTO_KMH, listw = listw)

qqnorm(data_agg_sf$MEAN_VIENTO_KMH)
qqline(data_agg_sf$MEAN_VIENTO_KMH, col=2)

data_agg_sf[56,]
Simple feature collection with 1 feature and 3 fields
Geometry type: POINT
Dimension:     XY
Bounding box:  xmin: -55.13333 ymin: -27.48333 xmax: -55.13333 ymax: -27.48333
Geodetic CRS:  WGS 84
   NOMBRE MEAN_VIENTO_KMH SD_VIENTO_KMH                    geometry
56  OBERA               4             0 POINT (-55.13333 -27.48333)
data_agg_sf[96,]
Simple feature collection with 1 feature and 3 fields
Geometry type: POINT
Dimension:     XY
Bounding box:  xmin: -62.73333 ymin: -35.96667 xmax: -62.73333 ymax: -35.96667
Geodetic CRS:  WGS 84
            NOMBRE MEAN_VIENTO_KMH SD_VIENTO_KMH                    geometry
96 TRENQUE LAUQUEN        10.33333       3.05505 POINT (-62.73333 -35.96667)
data_agg_sf = data_agg_sf[data_agg_sf$NOMBRE != "OBERA",]
data_agg_sf = data_agg_sf[data_agg_sf$NOMBRE != "TRENQUE LAUQUEN",]
rownames(data_agg_sf) <- 1:nrow(data_agg_sf)
# Creamos una lista de vecinos
knea <- knearneigh(data_agg_sf)
neib <- knn2nb(knea)
listw <- nb2listw(neib)

# Hacemos el test de moran 
moran_test_v2 <- moran.test(data_agg_sf$MEAN_VIENTO_KMH, listw)
moran_test_v2

    Moran I test under randomisation

data:  data_agg_sf$MEAN_VIENTO_KMH  
weights: listw    

Moran I statistic standard deviate = 4.8525, p-value = 6.096e-07
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.573582602      -0.009803922       0.014453793 
geary_test_v2 <- geary.test(data_agg_sf$MEAN_VIENTO_KMH, listw)
geary_test_v2

    Geary C test under randomisation

data:  data_agg_sf$MEAN_VIENTO_KMH 
weights: listw 

Geary C statistic standard deviate = 5.2198, p-value = 8.958e-08
alternative hypothesis: Expectation greater than statistic
sample estimates:
Geary C statistic       Expectation          Variance 
       0.33433216        1.00000000        0.01626356 
shaphiro_test_v2 <- shapiro.test(data_agg_sf$MEAN_VIENTO_KMH)
shaphiro_test_v2

    Shapiro-Wilk normality test

data:  data_agg_sf$MEAN_VIENTO_KMH
W = 0.96491, p-value = 0.007798
moran.plot(data_agg_sf$MEAN_VIENTO_KMH, listw = listw)

qqnorm(data_agg_sf$MEAN_VIENTO_KMH)
qqline(data_agg_sf$MEAN_VIENTO_KMH, col=2)

# CALCULAMOS EL COEFICIENTE DE MORAN Y EL DE GEARY
moran(data_agg_sf$MEAN_VIENTO_KMH, listw, length(listw$weights),Szero(listw),zero.policy = FALSE)
$I
[1] 0.5735826

$K
[1] 2.304703

#TODO: Agregar conclusion aca

plot(data_agg_sf$MEAN_VIENTO_KMH)

Variograma

v <- variogram(MEAN_VIENTO_KMH~1, data_agg_sf)
plot(v)

vt_exp = fit.variogram(v, vgm(125, "Exp", 30, 5))
vt_exp
plot(v , vt_exp)

vt_mat = fit.variogram(v, vgm(125, "Mat", 30, 5))
plot(v , vt_mat)

vt_exc = fit.variogram(v, vgm(125, "Exc", 30, 5))
No convergence after 200 iterations: try different initial values?
plot(v , vt_exc)

vt_bes = fit.variogram(v, vgm(125, "Bes", 30, 5))
plot(v , vt_bes)

attr(vt_exp, 'SSErr')
[1] 0.2612195
attr(vt_mat, 'SSErr')
[1] 0.2612195
attr(vt_exc, 'SSErr')
[1] 2.621681
attr(vt_bes, 'SSErr')
[1] 0.3501463

Kriging

departamentos <- st_read("data_departamentos/Codgeo_Pais_x_dpto_con_datos/pxdptodatosok.shp")
Reading layer `pxdptodatosok' from data source `/Users/antonellaschiavoni/Antonella/Maestria/EstadisticaEspacial/TPFinal/Datos/trabajo_final/smn/estadistica_espacial/data_departamentos/Codgeo_Pais_x_dpto_con_datos/pxdptodatosok.shp' using driver `ESRI Shapefile'
Simple feature collection with 527 features and 10 fields
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: -74.02985 ymin: -90 xmax: -25.02314 ymax: -21.74506
Geodetic CRS:  WGS 84
departamentos <-departamentos[departamentos$departamen != "Antártida Argentina",]
departamentos <-departamentos[departamentos$departamen != "Islas del Atlántico Sur",]
departamentos <- as_Spatial(departamentos)
grilla <- as.data.frame(spsample(departamentos, type="regular", n=5000))
CRS object has comment, which is lost in output
names(grilla) <- c("X", "Y")
coordinates(grilla) <- c("X", "Y")
plot(grilla)

gridded(grilla) <- TRUE
grid has empty column/rows in dimension 2
fullgrid(grilla) <- TRUE
plot(grilla)

proj4string(grilla) <- proj4string(departamentos)
CRS object has comment, which is lost in output
data_agg_sf <- as_Spatial(data_agg_sf)
proj4string(data_agg_sf) <- proj4string(departamentos)
CRS object has comment, which is lost in outputCRS object has comment, which is lost in output
ko1 <- krige(MEAN_VIENTO_KMH~1, data_agg_sf, grilla, model = vt_exp, nmax=20)
[using ordinary kriging]
spplot(ko1["var1.pred"])

spplot(ko1["var1.var"])


ko2 <- krige(MEAN_VIENTO_KMH~1, data_agg_sf, grilla, model = vt_exp, nmax=50)
[using ordinary kriging]
spplot(ko2["var1.pred"])

spplot(ko2["var1.var"])

ko3 <- krige(MEAN_VIENTO_KMH~1, data_agg_sf, grilla, model = vt_bes, nmax=50)
[using ordinary kriging]
spplot(ko3["var1.pred"])

spplot(ko3["var1.var"])

library(raster)

Attaching package: ‘raster’

The following object is masked from ‘package:dplyr’:

    select
r <- raster(ko1)
r.m <- mask(r, departamentos)
library(tmap)
tm_shape(r.m) +
  tm_raster(n=10, 
            palette="Blues",
            auto.palette.mapping=FALSE,
title="") +
tm_legend(legend.outside=TRUE)
The argument auto.palette.mapping is deprecated. Please use midpoint for numeric data and stretch.palette for categorical data to control the palette mapping.

r <- raster(ko1, layer="var1.var")
r.m <- mask(r, departamentos)

tm_shape(r.m) +
tm_raster(n=7, 
          palette ="Reds",
          title="Variance map ") +
tm_legend(legend.outside=TRUE)

r <- sqrt(raster(ko1, layer="var1.var")) * 1.96
r.m <- mask(r, departamentos)

tm_shape(r.m) +
tm_raster(n=7, 
          palette ="Reds",
          title="95% CI map \n(en km/h)") +
tm_legend(legend.outside=TRUE)

LS0tCnRpdGxlOiAiVHJhYmFqbyBGaW5hbCAtIEVzdGFkaXN0aWNhIEVzcGFjaWFsIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpDYXJnYW1vcyBsb3MgcGFxdWV0ZXMgbmVjZXNhcmlvcwpgYGB7cn0KbGlicmFyeShkcGx5cikKbGlicmFyeShzZikKbGlicmFyeShnc3RhdCkKbGlicmFyeShnZW9SKQpsaWJyYXJ5KHNwZGVwKQpsaWJyYXJ5KERhdGFFeHBsb3JlcikKbGlicmFyeShwc3ljaCkKbGlicmFyeShnZ2NvcnJwbG90KQpsaWJyYXJ5KGJpb2dlbykKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGxlYWZsZXQpCmBgYApDYXJnYW1vcyBsb3MgZGF0YXNldHMKYGBge3J9CmVzdGFjaW9uZXMgPC0gcmVhZC5jc3YoImRhdGFfc21uL3ByZXByb2Nlc3NlZC9lc3RhY2lvbmVzX3Ntbl92Mi5jc3YiKQpob3JhcmlvcyA8LSByZWFkLmNzdigiZGF0YV9zbW4vcHJlcHJvY2Vzc2VkL2RhdG9ob3JhcmlvMjAyMTA0MjAuY3N2IikKYGBgCgpPYnNlcnZhbW9zIGNvbW8gZXN0YSBjb21wdWVzdG8gZWwgZGF0YXNldApgYGB7cn0KZ2xpbXBzZShlc3RhY2lvbmVzKQpwcmludCgiIy0tLS0tLS0tLS0tLS0tLS0tLS0tIyIpCmdsaW1wc2UoaG9yYXJpb3MpCmBgYApPYnNlcnZhbW9zIHF1ZSBsYSB2YXJpYWJsZSBxdWUgcmVwcmVzZW50YSBsYSB2ZWxvY2lkYWQgZGVsIHZpZW50byBlbiBrbS9oIChGRikgZXMgZGUgdGlwbyBDaGFyLCBwb3IgbG8gY3VhbCBkZWJlbW9zIGNvbnZlcnRpbGEgZW4gaW50LgpgYGB7cn0KaG9yYXJpb3MkRkYgPC0gYXMubnVtZXJpYyhob3JhcmlvcyRGRikKYGBgCgpPYnNlcnZhbW9zIGVsIHJlc3VtZW4gZXN0YWRpc3RpY28gZGUgbGFzIHZhcmlhYmxlcyBkZSBwb3RlbmNpYWwgaW50ZXJlcwpgYGB7cn0KcHJpbnQoImFsdHVyYSIpCnN1bW1hcnkoZXN0YWNpb25lcyRBTFRVUkEpCnByaW50KCJob3JhIikKc3VtbWFyeShob3JhcmlvcyRIT1JBKQpwcmludCgidGVtcGVyYXR1cmEiKQpzdW1tYXJ5KGhvcmFyaW9zJFRFTVApCnByaW50KCJodW1lZGFkIikKc3VtbWFyeShob3JhcmlvcyRIVU0pCnByaW50KCJwcmVzaW9uIGF0bW9zZmVyaWNhIikKc3VtbWFyeShob3JhcmlvcyRQTk0pCnByaW50KCJkaXJlY2Npb24gZGVsIHZpZW50byIpCnN1bW1hcnkoaG9yYXJpb3MkREQpCnByaW50KCJ2ZWxvY2lkYWQgZGVsIHZpZW50byIpCnN1bW1hcnkoaG9yYXJpb3MkRkYpCmBgYApTZSBwdWVkZSBvYnNlcnZhciBsYSBwcmVzZW5jaWEgZGUgb2JzZXJ2YWNpb25lcyBOQSdzLCBwb3IgbG8gY3VhbCBwcm9jZWRlbW9zIGEgZWxpbWluYXJsYXMKYGBge3J9CndoaWNoKGlzLm5hKGhvcmFyaW9zKSwgYXJyLmluZD1UUlVFKQpgYGAKYGBge3J9CmhvcmFyaW9zWzc3MSxdCmBgYApDb21vIHRvZGEgbGEgZmlsYSBwYXJhIGxhIHJpb2phIGFlcm8gZXMgTkEsIGxsYSBlbGltaW5hbW9zCmBgYHtyfQpob3JhcmlvcyA8LSBob3Jhcmlvc1stYyg3NzEpLCBdICAKcm93bmFtZXMoaG9yYXJpb3MpIDwtIDE6bnJvdyhob3JhcmlvcykKYGBgCgpgYGB7cn0KaG9yYXJpb3NbNTg3LF0KYGBgCkNvbXBsZXRhbW9zIGxhIGNvbHVtbmEgaHVtZWRhZCBjb24gZWwgcHJvbWVkaW8KYGBge3J9CiMgRk9STU9TQSBBRVJPCmZvcm1vc2EgPC0gaG9yYXJpb3NbaG9yYXJpb3MkTk9NQlJFID09ICJGT1JNT1NBIEFFUk8iLF0Kd2hpY2goaXMubmEoZm9ybW9zYSksIGFyci5pbmQ9VFJVRSkKYGBgCgpgYGB7cn0KZm9ybW9zYSA8LSBuYS5vbWl0KGZvcm1vc2EpCmhvcmFyaW9zWzU4Nyw0XSA9IG1lYW4oZm9ybW9zYSRIVU0pCmhvcmFyaW9zWzU4NyxdCgpyZW1vdmUoZm9ybW9zYSkKYGBgCmBgYHtyfQp3aGljaChpcy5uYShob3JhcmlvcyksIGFyci5pbmQ9VFJVRSkKYGBgCgpgYGB7cn0KaG9yYXJpb3NbYygxODc1LDE4NzYpLF0KYGBgCkNvbXBsZXRhbW9zIGNvbiBlbCBwcm9tZWRpbyBkZSBodW1lZGFkIGNhbGN1bGFkbyBwYXJhIHR1Y3VtYW4gYWVybwpgYGB7cn0KIyBUVUNVTUFOIEFFUk8KdHVjdW1hbl9hZXJvIDwtIGhvcmFyaW9zW2hvcmFyaW9zJE5PTUJSRSA9PSAiVFVDVU1BTiBBRVJPIixdCndoaWNoKGlzLm5hKHR1Y3VtYW5fYWVybyksIGFyci5pbmQ9VFJVRSkKYGBgCmBgYHtyfQoKdHVjdW1hbl9hZXJvIDwtIG5hLm9taXQodHVjdW1hbl9hZXJvKQpob3Jhcmlvc1sxODc1LDRdID0gbWVhbih0dWN1bWFuX2Flcm8kSFVNKQpob3Jhcmlvc1sxODc2LDRdID0gbWVhbih0dWN1bWFuX2Flcm8kSFVNKQpob3Jhcmlvc1tjKDE4NzUsMTg3NiksXQoKcmVtb3ZlKHR1Y3VtYW5fYWVybykKYGBgCmBgYHtyfQpob3Jhcmlvc1syMzcsXQpgYGAKCmBgYHtyfQojIENBVEFNQVJDQSBBRVJPCmNhdGFtYXJjYV9hZXJvIDwtIGhvcmFyaW9zW2hvcmFyaW9zJE5PTUJSRSA9PSAiQ0FUQU1BUkNBIEFFUk8iLF0Kd2hpY2goaXMubmEoY2F0YW1hcmNhX2Flcm8pLCBhcnIuaW5kPVRSVUUpCmBgYApgYGB7cn0KY2F0YW1hcmNhX2Flcm8gPC0gbmEub21pdChjYXRhbWFyY2FfYWVybykKaG9yYXJpb3NbMjM3LDVdID0gbWVhbihjYXRhbWFyY2FfYWVybyRQTk0pCmhvcmFyaW9zWzIzNyxdCnJlbW92ZShjYXRhbWFyY2FfYWVybykKYGBgCmBgYHtyfQpob3Jhcmlvc1tjKDk3Miw5NzMsOTc0LDk3NSw5NzYsOTc3LDk3OCw5NzkpLF0KYGBgCgpgYGB7cn0KIyBNRVJDRURFUyBBRVJPCm1lcmNlZGVzX2Flcm8gPC0gaG9yYXJpb3NbaG9yYXJpb3MkTk9NQlJFID09ICJNRVJDRURFUyBBRVJPIixdCndoaWNoKGlzLm5hKG1lcmNlZGVzX2Flcm8pLCBhcnIuaW5kPVRSVUUpCnJlbW92ZShtZXJjZWRlc19hZXJvKQpgYGAKVmFtb3MgYSBjb21wbGV0YXIgbG9zIHZhbG9yZXMgZmFsdGFudGVzIHBhcmEgbGEgcHJlc2lvbiBhdG1vc2ZlcmljYSBjb24gZWwgcHJvbWVkaW8gZGUgbGEgcHJvdmluY2lhIGRlIGNvcnJpZW50ZXMgYSBsYSBxdWUgcGVydGVuZWNlIE1lcmNlZGVzIGFlcm8KYGBge3J9CmNvcnJpZW50ZXNfYWVybyA8LSBob3Jhcmlvc1soaG9yYXJpb3MkTk9NQlJFID09ICJDT1JSSUVOVEVTIEFFUk8iKSB8IChob3JhcmlvcyROT01CUkUgPT0gIklUVVpBSU5HTyIpIHwgKGhvcmFyaW9zJE5PTUJSRSA9PSAiTU9OVEUgQ0FTRVJPUyBBRVJPIikgfCAoaG9yYXJpb3MkTk9NQlJFID09ICJQQVNPIERFIExPUyBMSUJSRVMgQUVSTyIpLF0Kd2hpY2goaXMubmEoY29ycmllbnRlc19hZXJvKSwgYXJyLmluZD1UUlVFKQoKaG9yYXJpb3NbOTcyLDVdID0gbWVhbihjb3JyaWVudGVzX2Flcm8kUE5NKQpob3Jhcmlvc1s5NzMsNV0gPSBtZWFuKGNvcnJpZW50ZXNfYWVybyRQTk0pCmhvcmFyaW9zWzk3NCw1XSA9IG1lYW4oY29ycmllbnRlc19hZXJvJFBOTSkKaG9yYXJpb3NbOTc1LDVdID0gbWVhbihjb3JyaWVudGVzX2Flcm8kUE5NKQpob3Jhcmlvc1s5NzYsNV0gPSBtZWFuKGNvcnJpZW50ZXNfYWVybyRQTk0pCmhvcmFyaW9zWzk3Nyw1XSA9IG1lYW4oY29ycmllbnRlc19hZXJvJFBOTSkKaG9yYXJpb3NbOTc4LDVdID0gbWVhbihjb3JyaWVudGVzX2Flcm8kUE5NKQpob3Jhcmlvc1s5NzksNV0gPSBtZWFuKGNvcnJpZW50ZXNfYWVybyRQTk0pCgpob3Jhcmlvc1tjKDk3Miw5NzMsOTc0LDk3NSw5NzYsOTc3LDk3OCw5NzkpLF0KcmVtb3ZlKGNvcnJpZW50ZXNfYWVybykKYGBgCmBgYHtyfQpob3Jhcmlvc1tjKDEzOTEsMTM5MiwxMzk0LDEzOTUsMTM5NiwxMzk3LDEzOTgsMTM5OSwxNDAwLDE0MDEsMTQwMiwxNDAzKSxdCmBgYAoKYGBge3J9CiMgUklPIEdBTExFR09TIEFFUk8KZ2FsbGVnb3NfYWVybyA8LSBob3Jhcmlvc1tob3JhcmlvcyROT01CUkUgPT0gIlJJTyBHQUxMRUdPUyBBRVJPIixdCndoaWNoKGlzLm5hKGdhbGxlZ29zX2Flcm8pLCBhcnIuaW5kPVRSVUUpCmBgYApgYGB7cn0KZ2FsbGVnb3NfYWVybyA8LSBuYS5vbWl0KGdhbGxlZ29zX2Flcm8pCmhvcmFyaW9zWzEzOTEsNV0gPSBtZWFuKGdhbGxlZ29zX2Flcm8kUE5NKQpob3Jhcmlvc1sxMzkyLDVdID0gbWVhbihnYWxsZWdvc19hZXJvJFBOTSkKaG9yYXJpb3NbMTM5Myw1XSA9IG1lYW4oZ2FsbGVnb3NfYWVybyRQTk0pCmhvcmFyaW9zWzEzOTQsNV0gPSBtZWFuKGdhbGxlZ29zX2Flcm8kUE5NKQpob3Jhcmlvc1sxMzk1LDVdID0gbWVhbihnYWxsZWdvc19hZXJvJFBOTSkKaG9yYXJpb3NbMTM5Niw1XSA9IG1lYW4oZ2FsbGVnb3NfYWVybyRQTk0pCmhvcmFyaW9zWzEzOTcsNV0gPSBtZWFuKGdhbGxlZ29zX2Flcm8kUE5NKQpob3Jhcmlvc1sxMzk4LDVdID0gbWVhbihnYWxsZWdvc19hZXJvJFBOTSkKaG9yYXJpb3NbMTM5OSw1XSA9IG1lYW4oZ2FsbGVnb3NfYWVybyRQTk0pCmhvcmFyaW9zWzE0MDAsNV0gPSBtZWFuKGdhbGxlZ29zX2Flcm8kUE5NKQpob3Jhcmlvc1sxNDAxLDVdID0gbWVhbihnYWxsZWdvc19hZXJvJFBOTSkKaG9yYXJpb3NbMTQwMiw1XSA9IG1lYW4oZ2FsbGVnb3NfYWVybyRQTk0pCmhvcmFyaW9zWzE0MDMsNV0gPSBtZWFuKGdhbGxlZ29zX2Flcm8kUE5NKQpob3Jhcmlvc1tjKDEzOTEsMTM5MiwxMzk0LDEzOTUsMTM5NiwxMzk3LDEzOTgsMTM5OSwxNDAwLDE0MDEsMTQwMiwxNDAzKSxdCnJlbW92ZShnYWxsZWdvc19hZXJvKQpgYGAKYGBge3J9CndoaWNoKGlzLm5hKGhvcmFyaW9zKSwgYXJyLmluZD1UUlVFKQpgYGAKYGBge3J9CmhvcmFyaW9zWzE0NDAsXQpgYGAKYGBge3J9CiMgUklPIEdSQU5ERQpyaW9fZ3JhbmRlX2Flcm8gPC0gaG9yYXJpb3NbaG9yYXJpb3MkTk9NQlJFID09ICJSSU8gR1JBTkRFIEIuQS4iLF0Kd2hpY2goaXMubmEocmlvX2dyYW5kZV9hZXJvKSwgYXJyLmluZD1UUlVFKQpgYGAKYGBge3J9CnJpb19ncmFuZGVfYWVybyA8LSBuYS5vbWl0KHJpb19ncmFuZGVfYWVybykKaG9yYXJpb3NbMTQ0MCw1XSA9IG1lYW4ocmlvX2dyYW5kZV9hZXJvJFBOTSkKaG9yYXJpb3NbMTQ0MCxdCgpyZW1vdmUocmlvX2dyYW5kZV9hZXJvKQpgYGAKVmFsaWRhbW9zIHF1ZSBlZmVjdGl2YW1lbnRlIHNlIGhheWFuIGFycmVnbGFkbwpgYGB7cn0KcHJpbnQoInRlbXBlcmF0dXJhIikKc3VtbWFyeShob3JhcmlvcyRURU1QKQpwcmludCgiaHVtZWRhZCIpCnN1bW1hcnkoaG9yYXJpb3MkSFVNKQpwcmludCgicHJlc2lvbiBhdG1vc2ZlcmljYSIpCnN1bW1hcnkoaG9yYXJpb3MkUE5NKQpwcmludCgiZGlyZWNjaW9uIGRlbCB2aWVudG8iKQpzdW1tYXJ5KGhvcmFyaW9zJEREKQpwcmludCgidmVsb2NpZGFkIGRlbCB2aWVudG8iKQpzdW1tYXJ5KGhvcmFyaW9zJEZGKQpgYGAKUGVyZmVjdG8sIHlhIG5vIHRlbmVtb3MgdmFsb3JlcyBudWxvcyBlbiBudWVzdHJvIGRhdGFzZXQuCgojIEFuYWxpc2lzIGRlc2NyaXB0aXZvIGRlIGxhcyB2YXJpYWJsZXMKCkEgY29udGludWFjacOzbiwgdmlzdWFsaXphcmVtb3MgbGFzIGRpc3RyaWJ1Y2lvbmVzIGRlIGxhcyB2YXJpYWJsZXMgbnVtZXJpY2FzIGRlIGludGVyw6lzCmBgYHtyfQpoaXN0KGVzdGFjaW9uZXMkQUxUVVJBLCBtYWluID0gIkhpc3RvZ3JhbWEgZGUgbGEgYWx0dXJhIiwgeGxhYiA9ICJBbHR1cmEiKQpoaXN0KGhvcmFyaW9zJEhPUkEsIG1haW4gPSAiSGlzdG9ncmFtYSBkZSBob3JhcmlvcyIsIHhsYWIgPSAiSG9yYXJpb3MiKQpoaXN0KGhvcmFyaW9zJFRFTVAsIG1haW4gPSAiSGlzdG9ncmFtYSBkZSB0ZW1wZXJhdHVyYSIsIHhsYWIgPSAiVGVtcGVyYXR1cmEiKQpoaXN0KGhvcmFyaW9zJEhVTSwgbWFpbiA9ICJIaXN0b2dyYW1hIGRlIGh1bWVkYWQiLCB4bGFiID0gIkh1bWVkYWQiKQpoaXN0KGhvcmFyaW9zJFBOTSwgbWFpbiA9ICJIaXN0b2dyYW1hIGRlIHByZXNpb24gYXRtb3NmZXJpY2EiLCB4bGFiID0gIlByZXNpb24gQXRtb3NmZXJpY2EiKQpoaXN0KGhvcmFyaW9zJERELCBtYWluID0gIkhpc3RvZ3JhbWEgZGUgbGEgZGlyZWNjaW9uIGRlbCB2aWVudG8iLCB4bGFiID0gIkRpcmVjY2lvbiBkZWwgdmllbnRvIikKaGlzdChob3JhcmlvcyRGRiwgbWFpbiA9ICJIaXN0b2dyYW1hIGRlIGxhIHZlbG9jaWRhZCBkZWwgdmllbnRvIiwgeGxhYiA9ICJWZWxvY2lkYWQgZGVsIHZpZW50byIpCmBgYApDcmVhbW9zIHVub3MgYm94cGxvdCBwYXJhIHZpc3VhbGl6YXIgbGEgZGlzdHJpYnVjaW9uZGUgZGF0b3MgcXVlIHBvdGVuY2lhbG1lbnRlIG5vcyBpbnRlcmVjZW4gcGFyYSBwcm9jZWRlciBjb24gZWwgYW5hbGlzaXMKYGBge3J9CmJveHBsb3QoZXN0YWNpb25lcyRBTFRVUkEsIG1haW4gPSAiQm94cGxvdCBkZSBsYSBhbHR1cmEiLCB4bGFiID0gIkFsdHVyYSIpCmJveHBsb3QoaG9yYXJpb3MkSE9SQSwgbWFpbiA9ICJCb3hwbG90IGRlIGhvcmFyaW9zIiwgeGxhYiA9ICJIb3JhcmlvcyIpCmJveHBsb3QoaG9yYXJpb3MkVEVNUCwgbWFpbiA9ICJCb3hwbG90IGRlIHRlbXBlcmF0dXJhIiwgeGxhYiA9ICJUZW1wZXJhdHVyYSIpCmJveHBsb3QoaG9yYXJpb3MkSFVNLCBtYWluID0gIkJveHBsb3QgZGUgaHVtZWRhZCIsIHhsYWIgPSAiSHVtZWRhZCIpCmJveHBsb3QoaG9yYXJpb3MkUE5NLCBtYWluID0gIkJveHBsb3QgZGUgcHJlc2lvbiBhdG1vc2ZlcmljYSIsIHhsYWIgPSAiUHJlc2lvbiBBdG1vc2ZlcmljYSIpCmJveHBsb3QoaG9yYXJpb3MkREQsIG1haW4gPSAiQm94cGxvdCBkZSBsYSBkaXJlY2Npb24gZGVsIHZpZW50byIsIHhsYWIgPSAiRGlyZWNjaW9uIGRlbCB2aWVudG8iKQpib3hwbG90KGhvcmFyaW9zJEZGLCBtYWluID0gIkJveHBsb3QgZGUgbGEgdmVsb2NpZGFkIGRlbCB2aWVudG8iLCB4bGFiID0gIlZlbG9jaWRhZCBkZWwgdmllbnRvIikKYGBgCkxvcyBib3hwbG90cyBhbnRlcmlvcmVzIHBvbmVuIGVuIGV2aWRlbmNpYSBsYSBleGlzdGVuY2lhIGRlIG91dGxpZXJzLiDCv1Blcm8gc29uIGVzdG9zIHJlYWxtZW50ZSBvdXRsaWVycywgbyBwZXJ0ZW5lY2VuIGEgb2JzZXJ2YWNpb25lcyBlbiBsdWdhcmVzIG11eSByZW1vdG9zPyBFc3RvIGxvIGFuYWxpemFyZW1vcyBsdWVnbywgYWwgbW9tZW50byBkZSBncmFmaWNhciBsYXMgZXN0YWNpb25lcyBlbiBlbCBtYXBhIGRlIEFyZ2VudGluYS4KCkFob3JhLCB2ZWFtb3MgcXVlIHRhbiBjb3JyZWxhY2lvbmFkYXMgZXN0YW4gZXN0YXMgdmFyaWFibGVzLgpgYGB7cn0KY29yciA8LSBjb3IoaG9yYXJpb3NbLCBjKDIsMyw0LDUsNiw3KV0sIHVzZSA9ICJjb21wbGV0ZS5vYnMiKQoKZ2djb3JycGxvdChjb3JyLCB0eXBlID0gImxvd2VyIiwgb3V0bGluZS5jb2wgPSAiYmxhY2siLAogbGFiPVRSVUUsCiBnZ3RoZW1lID0gZ2dwbG90Mjo6dGhlbWVfZ3JheSwKIGNvbG9ycyA9IGMoIiM2RDlFQzEiLCAid2hpdGUiLCAiI0U0NjcyNiIpKQpgYGAKTGFzIHZhcmlhYmxlcyBxdWUgbWFzIGNvcnJlbGFjaW9uYW4gY29uIGxhIHZlbG9jaWRhZCBkZWwgdmllbnRvIHNvbiBIVU1FREFEIChuZWdhdGl2YW1lbnRlKSB5IEhPUkEgKHBvc2l0aXZhbWVudGUpLgpUYW1iaWVuIHZlbW9zIHF1ZSBIT1JBIHkgVEVNUEVSQVRVUkEgY29ycmVsYWNpb25hbiBuZWdhdGl2YW1lbnRlY29uIEhVTUVEQUQuClBvciB1bHRpbW8sIHNlIG9ic2VydmEgcXVlIEhPUkEgeSBURU1QRVJBVFVSQSBjb3JyZWxhY2lvbmFuIHBvc2l0aXZhbWVudGUKCkFuYWxpemFtb3MgYWhvcmEgbGEgc2ltZXRyaWEgZGUgbGEgdmFyaWFibGUgcXVlIHJlcHJlc2VudGEgbGEgdmVsb2NpZGFkIGRlbCB2aWVudG8geWEgcXVlIGVzIGxhIHF1ZSBtYXMgbm9zIGludGVyZXNhIGVuIGVzdGUgZXN0dWRpby4KYGBge3J9CnNrZXcoaG9yYXJpb3MkRkYpCmt1cnRvc2koaG9yYXJpb3MkRkYpCmBgYApMYSBtZWRpZGEgZGUgYXNpbWV0cmlhIHkga3VydG9zaSB0ZXJtaW5hbiBkZSB2YWxpZGFyIGxvIHF1ZSBvYnNlcnZhbW9zIGVuIGVsIGhpc3RvZ3JhbWEuIExhIHZhcmlhYmxlIEZGIGVzIGFzaW1ldHJpY2EgYSBkZXJlY2hhIHkgdGllbmUgdW5hIG1heW9yIGNvbmNlbnRyYWNpb24gZGUgdmFsb3JlcyBtdXkgY2VyY2EgZGUgbGEgbWVkaWEgZGUgbGEgZGlzdHJpYnVjacOzbiB5IG11eSBsZWpvcyBkZSBsYSBjb2xhIGRlIGxhIGRpc3RyaWJ1Y2lvbi4KCiMgUHJlcHJvY2VzYW1pZW50byBkZSBkYXRhc2V0ClVuIGRldGFsbGUgbm8gbWVub3IgZGVsIGRhdGFzZXQgZGUgZXN0YWNpb25lcyBlcyBxdWUgbGFzIGxhdGl0dWRlcyB5IGxvbmdpdHVkZXMgZXN0YW4gZXhwcmVzYWRhZHMgZW4gZ3JhZG9zIHkgbWludXRvcy4gUGFyYSBwb2RlciB0cmFiYWphciBjb24gZWxsYXMsIG5lY2VzaXRhbW9zIHF1ZSBlc3RlbiBleHByZXNhZGFzIGVuIHZhbG9yZXMgZGVjaW1hbGVzLiBQb3IgZXNvLCBlbiBlbCBzaWd1aWVudGUgYmxvcXVlIGRlIGPDs2RpZ28gdmFtb3MgYSB1c2FyIGxhIGZ1bmNpb24gZG1zMmRkIHBhcmEgaGFjZXIgZXN0YSBjb252ZXJzacOzbi4KYGBge3J9CiMgY3JlYW1vcyBkb3MgdmVjdG9yZXMgdmFjaW9zCmxhdGl0dWQgPC0gYygpCmxvbmdpdHVkIDwtIGMoKQoKIyBpdGVyYW1vcyBwb3IgY2FkYSBmaWxhIGRlbCBkYXRhc2V0IGRlIGVzdGFjaW9uZXMgeSBoYWNlbW9zIGxhIGNvbnZlc2lvbiBkZSBsYXRpdHVkIHkgbG9uZ2l0dWQKZm9yKGkgaW4gMTpucm93KGVzdGFjaW9uZXMpKSB7CiAgICAgbGF0aXR1ZFtpXSA8LSBkbXMyZGQoZGQgPSBlc3RhY2lvbmVzW2ksICJMQVRJVFVEX0dSQURPUyJdLCBtbSA9IGVzdGFjaW9uZXNbaSwgIkxBVElUVURfTUlOVVRPUyJdLCBzcyA9IDAsIG5zID0gIlMiKQogICAgIGxvbmdpdHVkW2ldIDwtIGRtczJkZChkZCA9IGVzdGFjaW9uZXNbaSwgIkxPTkdJVFVEX0dSQURPUyJdLCBtbSA9IGVzdGFjaW9uZXNbaSwgIkxPTkdJVFVEX01JTlVUT1MiXSwgc3MgPSAwLCBucyA9ICJTIikKfQoKIyBhc2lnbmFtb3MgYSBsYXRpdHVkIHkgbG9uZ2l0dWQgbG9zIHZhbG9yZXMgY29udmVydGlkb3MKZXN0YWNpb25lc1snTEFUSVRVRCddIDwtIGxhdGl0dWQKZXN0YWNpb25lc1snTE9OR0lUVUQnXSA8LSBsb25naXR1ZApgYGAKQW50ZXMgZGUgdW5pciBlc3RhY2lvbmVzLCBzZSByZW1vdmVyYW4gbGFzIGNvbHVtbmFzIHF1ZSBubyBzZWFuIHJlbGV2YW50ZXMgcGFyYSBlc3RlIGFuYWxpc2lzLiBMYXMgbWlzbWFzIHNvbiBOUk8geSBOcm9PQUNJLCBMQVRJVFVEX0dSQURPUywgTEFUSVRVRF9NSU5VVE9TLCBMT05HSVRVRF9HUkFET1MsIExPTkdJVFVEX01JTlVUT1MuIEFzaSBjb21vIHRhbWJpZW4gc2UgcmVtb3ZlcmEgbGEgdmFyaWFibGUgZmVjaGEsIHlhIHF1ZSBlc3RvcyBkYXRvcyBwZXJ0ZW5lY2VuIGFsIDIwLzA0LzIwMjEKYGBge3J9CmVzdGFjaW9uZXMgPC0gZXN0YWNpb25lc1tjKDEsMiw3LDEwLDExKV0KaG9yYXJpb3MgPC0gaG9yYXJpb3NbLGMoMyw0LDUsNiw3LDgpXQpgYGAKCmBgYHtyfQpzdW1tYXJ5KGVzdGFjaW9uZXMpCnN1bW1hcnkoaG9yYXJpb3MkVEVNUCkKYGBgCgpTZSBwcm9jZWRlIGEgdW5pZmljYXIgbGFzIGRvcyB0YWJsYXMgdXNhbmRvIGxhIHZhcmlhYmxlIE5PTUJSRSBjb21vIHB1bnRvIHBhcmEgY29tYmluYXIgbG9zIGRhdGFzZXRzCmBgYHtyfQpkYXRhIDwtIGlubmVyX2pvaW4oZXN0YWNpb25lcywgaG9yYXJpb3MsIGJ5ID0gYygiTk9NQlJFIiA9ICJOT01CUkUiKSkKCmdsaW1wc2UoZGF0YSkKYGBgCmBgYHtyfQpzdW1tYXJ5KGRhdGEkTk9NQlJFKQpzdW1tYXJ5KGRhdGEkUFJPVklOQ0lBKQpzdW1tYXJ5KGRhdGEkQUxUVVJBKQpzdW1tYXJ5KGRhdGEkTEFUSVRVRCkKc3VtbWFyeShkYXRhJExPTkdJVFVEKQpzdW1tYXJ5KGRhdGEkVEVNUCkKc3VtbWFyeShkYXRhJEhVTSkKc3VtbWFyeShkYXRhJFBOTSkKc3VtbWFyeShkYXRhJEREKQpzdW1tYXJ5KGRhdGEkRkYpCmBgYApWb2x2ZW1vcyByZWFsaXphciBlbCBncmFmaWNvIGRlIGNvcnJlbGFjaW9uIGluY2x1eWVuZG8gbGEgdmFyaWFibGUgYWx0dXJhLgpgYGB7cn0KY29yciA8LSBjb3IoZGF0YVssIGMoMyw0LDUsNiw3LDgsOSwxMCldLCB1c2UgPSAiY29tcGxldGUub2JzIikKCmdnY29ycnBsb3QoY29yciwgdHlwZSA9ICJsb3dlciIsIG91dGxpbmUuY29sID0gImJsYWNrIiwKIGxhYj1UUlVFLAogZ2d0aGVtZSA9IGdncGxvdDI6OnRoZW1lX2dyYXksCiBjb2xvcnMgPSBjKCIjNkQ5RUMxIiwgIndoaXRlIiwgIiNFNDY3MjYiKSkKYGBgCiMgVE9ETzogQUdSRUdBUiBDT05DTFVTSU9OIERFTCBHUkFGSUNPIERFIENPUlJFTEFDSU9OCgpTZSBvYnNlcnZhIHF1ZSBoYXkgZXN0YWNpb25lcyBxdWUgdGllbmUgbGFzIG9ic2VydmFjaW9uZXMgZW4gMCBwYXJhIGxhIHZhcmlhYmxlIEZGIHkgREQuIENvbnNpZGVyYW1vcyBlc3RvIGNvbW8gdW4gZXJyb3IgZW4gZWwgaW5zdHJ1bWVudG8gZGUgbWVkaWNpb24sIHBvciBsbyBjdWFsIHZhbW9zIGEgZWxpbWluYXIgYSBlc2EgZXN0YWNpb24gZGVsIGFuYWxpc2lzLgpgYGB7cn0KZGF0YSA9IGRhdGFbKGRhdGEkRkYgIT0gMCkgJiAoZGF0YSRERCAhPSAwKSxdCnJvd25hbWVzKGRhdGEpIDwtIDE6bnJvdyhkYXRhKQpgYGAKCkFncnVwYW1vcyBsb3MgZGF0b3MgcG9yIG5vbWJyZSBjYWxjdWxhbmRvIGVsIHByb21lZGlvIHkgZGVzdmlvIGRlbCB2aWVudG8KYGBge3J9CmRhdGFfYWdnID0gZGF0YSAlPiUKICBncm91cF9ieShOT01CUkUpICU+JQogIHN1bW1hcmlzZShNRUFOX1ZJRU5UT19LTUggPSBtZWFuKEZGKSwgCiAgICAgICAgICAgIFNEX1ZJRU5UT19LTUggPSBzZChGRiksIAogICAgICAgICAgICBMT05HSVRVRCA9IHVuaXF1ZShMT05HSVRVRCksIAogICAgICAgICAgICBMQVRJVFVEID0gdW5pcXVlKExBVElUVUQpLCAKICAgICAgICAgICAgLmdyb3VwcyA9ICJrZWVwIikKYGBgClZlbW9zIHF1ZSBlbiBlbCBkYXRhc2V0IHJlc3VsdGFudGUgbm9zIHF1ZWRhbiA5OCBvYnNlcnZhY2lvbmVzIHF1ZSBjb2luY2lkZW4gY29uIGxhIGNhbnRpZGFkIGRlIGVzdGFjaW9uZXMgbWV0ZW9yb2xvZ2ljYXMgb3JpZ2luYWxlcwoKCkNvbnZlcnRpbW9zIGRhdGFfYWdnIGEgZGF0YS5mcmFtZSB5YSBxdWUgbmVjZXNpdGFtb3MgZXN0ZSB0aXBvIGRlIGRhdG8gcGFyYSBwb2RlciB0cmFiYWphcgpgYGB7cn0KZGF0YV9hZ2cgPSBkYXRhLmZyYW1lKGRhdGFfYWdnKQpgYGAKClRyYW5zZm9ybWFtb3MgZGZfZGF0YV9hZ2cgZW4gdW4gYXJjaGl2byBnZW9ncsOhZmljbyB1dGlsaXphbmRvIGVsIGPDs2RpZ28gZGUgcHJveWVjY2nDs24gbWVyY2F0b3IKYGBge3J9CmRhdGFfYWdnX3NmID0gc3RfYXNfc2YoZGF0YV9hZ2csIGNvb3JkcyA9IGMoIkxPTkdJVFVEIiwgIkxBVElUVUQiKSwgY3JzID0gNDMyNikKYGBgCgpWYWxpZGFtb3MgbGEgY2xhc2UgZGVsIG51ZXZvIGRhdGFmcmFtZQpgYGB7cn0KY2xhc3MoZGF0YV9hZ2dfc2YpCmBgYAoKIyBBbmFsaXNpcyBleHBsb3JhdG9yaW8gZXNwYWNpYWwKCiMjIEdyYWZpY28gZGUgbGFzIGVzdGFjaW9uZXMgbWV0ZW9yb2xvZ2lhcwpFbiBlbCBzaWd1aWVudGUgZ3LDoWZpY28gZGUgbGEgcmVwdWJsaWNhIGFyZ2VudGluYSBzZSBvYnNlcnZhbiBlbiBjb2xvciBhenVsIGxhcyBlc3RhY2lvbmVzIG1ldGVvcm9sw7NnaWNhcyBkb25kZSBzZSByZWFsaXphcm9uIGxhcyBtZWRpY2lvbmVzIGRlIGxhIHZhcmlhYmxlcyBxdWUgZXN0YW4gcHJlc2VudGVzIGVuIGVsIGRhdGFzZXQKClF1ZXJlbW9zIHF1ZSBlbiBlbCBtYXBhIHNlIHZlYSBjb21vIGV0aXF1ZXRhIGVsIG5vbWJyZSBkZSBsYSBiYXNlIG1ldGVvcm9sb2dpY2EuIFBhcmEgZXNvIGFwbGljYW1vcyBsYSBzaWd1aWVudGUgZnVuY2lvbgpgYGB7cn0KbGFicyA8LSBsYXBwbHkoc2VxKG5yb3coZGF0YV9hZ2dfc2YpKSwgZnVuY3Rpb24oaSkgewogIHBhc3RlMCggJzxwPicsIGRhdGFfYWdnX3NmW2ksICJOT01CUkUiXSwgJzxwPicsICc8cD4nLGRhdGFfYWdnX3NmW2ksICJNRUFOX1ZJRU5UT19LTUgiXSwnPC9wPicgKSB9KQpgYGAKClJlYWxpemFtb3MgZWwgZ3JhZmljbyBpbnRlcmFjdGl2byBkZSBsYXMgZXN0YWNpb25lcyBtZXRlb3JvbG9naWNhcyBncmFmaWNhZGFzIHNvYnJlIHVuIG1hcGEgZGUgQXJnZW50aW5hLgpgYGB7cn0KbGVhZmxldCgpICU+JQogIGFkZFRpbGVzKCkgJT4lCiAgYWRkQ2lyY2xlcyhkYXRhID0gZGF0YV9hZ2dfc2YsIHdlaWdodCA9IDMsIGxhYmVsID0gbGFwcGx5KGxhYnMsIGh0bWx0b29sczo6SFRNTCkpCgpgYGAKCiMgTGltcGllemEgZGUgZGF0b3MKRW4gZWwgbWFwYSBvYnNlcnZhbW9zIHF1ZSBoYXkgcHVudG9zIG11eSBkaXN0YW50ZXMgZGUgbGEgQXJnZW50aW5hIGNvbnRpbmVudGFsLiBEYWRvIGVsIHByb3Bvc2l0byBkZSBlc3RlIGVzdHVkaW8sIGVsIGN1YWwgZXMgZGV0ZXJtaW5hciBsYSB1YmljYWNpb24gZ2VvZ3JhZmljYSDDs3B0aW1hIGVuIGJhc2UgYSBsYSB2YXJpYWJsZSB2ZWxvY2lkYWQgZGVsIHZpZW50bywgZGVjaWRpbW9zIHJlbW92ZXIgZXN0YXMgb2JzZXJ2YWNpb25lcyB5YSBxdWUgbm8gYXBvcnRhIGluZm9ybWFjaW9uIHV0aWwgeSBhZGVtYXMgYWdyZWdhbiBydWlkbyBhIG51ZXN0cm8gYW5hbGlzaXMuCgpQcmltZXJvLCB2YW1vcyBhIGJvcnJhciBsYXMgZXN0YWNpb25lcyBxdWUgbm8gZXN0YW4gZW4gbGEgcGxhdGFmb3JtYSBjb250aW5lbnRhbCBhcmdlbnRpbmEKLSBCYXNlIENhcmxpbmkKLSBCYXNlIFNhbiBNYXJ0aW4KLSBCYXNlIE1hcmFtYmlvCi0gQmFzZSBFc3BlcmFuemEKLSBCYXNlIE9yY2FkYXMKCmBgYHtyfQpkYXRhX2FnZ19zZiA9IGRhdGFfYWdnX3NmW2RhdGFfYWdnX3NmJE5PTUJSRSAhPSAiQkFTRSBDQVJMSU5JIChFWCBKVUJBTlkpIixdCmRhdGFfYWdnX3NmID0gZGF0YV9hZ2dfc2ZbZGF0YV9hZ2dfc2YkTk9NQlJFICE9ICJCQVNFIFNBTiBNQVJUSU4iLF0KZGF0YV9hZ2dfc2YgPSBkYXRhX2FnZ19zZltkYXRhX2FnZ19zZiROT01CUkUgIT0gIkJBU0UgTUFSQU1CSU8iLF0KZGF0YV9hZ2dfc2YgPSBkYXRhX2FnZ19zZltkYXRhX2FnZ19zZiROT01CUkUgIT0gIkJBU0UgRVNQRVJBTlpBIixdCmRhdGFfYWdnX3NmID0gZGF0YV9hZ2dfc2ZbZGF0YV9hZ2dfc2YkTk9NQlJFICE9ICJCQVNFIE9SQ0FEQVMiLF0KZGF0YV9hZ2dfc2YgPSBkYXRhX2FnZ19zZltkYXRhX2FnZ19zZiROT01CUkUgIT0gIkJBU0UgQkVMR1JBTk8gSUkiLF0KYGBgCgpSZXBldGltb3MgZWwgcGxvdCBwYXJhIHZhbGlkYXIKYGBge3J9CmxhYnMgPC0gbGFwcGx5KHNlcShucm93KGRhdGFfYWdnX3NmKSksIGZ1bmN0aW9uKGkpIHsKICBwYXN0ZTAoICc8cD4nLCBkYXRhX2FnZ19zZltpLCAiTk9NQlJFIl0sICc8L3A+JyApIH0pCgoKbGVhZmxldCgpICU+JQogIGFkZFRpbGVzKCkgJT4lCiAgYWRkQ2lyY2xlcyhkYXRhID0gZGF0YV9hZ2dfc2YsIHdlaWdodCA9IDMsIGxhYmVsID0gbGFwcGx5KGxhYnMsIGh0bWx0b29sczo6SFRNTCkpCgpgYGAKCk9ic2VydmVtb3MgZWwgcmVzdW1lbiBlc3RhZGlzdGljbyBkZSBsYXMgbnVldmFzIHZhcmlhYmxlcyBNRUFOX1ZJRU5UT19LTUggeSBTRF9WSUVOVE9fS01IIApgYGB7cn0KZGVzY3JpYmUoZGF0YV9hZ2dfc2YkTUVBTl9WSUVOVE9fS01IKQpoaXN0KGRhdGFfYWdnX3NmJE1FQU5fVklFTlRPX0tNSCkKYm94cGxvdChkYXRhX2FnZ19zZiRNRUFOX1ZJRU5UT19LTUgpCmBgYApgYGB7cn0KZGVzY3JpYmUoZGF0YV9hZ2dfc2YkU0RfVklFTlRPX0tNSCkKaGlzdChkYXRhX2FnZ19zZiRTRF9WSUVOVE9fS01IKQpib3hwbG90KGRhdGFfYWdnX3NmJFNEX1ZJRU5UT19LTUgpCmBgYApWZWFtb3Mgc2kgZXN0YSBudWV2YSB2YXJpYWJsZSBNRUFOX1ZJRU5UT19LTUggZXMgbm9ybWFsLgpgYGB7cn0KaGlzdChkYXRhX2FnZ19zZiRNRUFOX1ZJRU5UT19LTUgpCmJveHBsb3QoZGF0YV9hZ2dfc2YkTUVBTl9WSUVOVE9fS01IKQpxcW5vcm0oZGF0YV9hZ2dfc2YkTUVBTl9WSUVOVE9fS01IKQpxcWxpbmUoZGF0YV9hZ2dfc2YkTUVBTl9WSUVOVE9fS01ILCBjb2w9MikKc2hhcGlyby50ZXN0KGRhdGFfYWdnX3NmJE1FQU5fVklFTlRPX0tNSCkKYGBgCkNsYXJhbWVudGUgbGEgdmFyaWFibGUgTUVBTl9WSUVOVE9fS01IIG5vIGVzIG5vcm1hbC4gRWwgcXFwbG90IHBvbmUgZW4gZXZpZGVuY2lhIGxhIGV4aXN0ZW5jaWEgZGUgY29sYXMgcGVzYWRhcy4gQWRlbWFzLCBhbCByZWFsaXphciBlbCB0ZXN0IGRlIHNoYXBpcm8gd2lsayBlbCBwLXZhbHVlIG9idGVuaWRvIGVzIG1lbm9yIGEgMC4wNSwgbG8gY3VhbCBpbmRpY2EgcXVlIGxvcyBkYXRvcyBxdWUgdGVuZW1vcyBubyBzb24gbm9ybWFsZXMKI1RPRE86IGFncmVnYXIgYWxnbyBtYXMgYSBlc3RhIGNvbmNsdXNpb24/CgpBaG9yYSwgcHJvY2VkZW1vcyBhIGFuYWxpemFyIGxhIGV4aXN0ZW5jaWEgZGUgaW5saWVycywgeSBlbiBlbCBjYXNvIGRlIGVuY29udHJhcmxvcywgZWxpbWluYXJsb3MuUGFyYSBlc28sIHVzYW1vcyBlbCB0ZXN0IGRlIG1vcmFuLiBCYXNpY2FtZW50ZSBsbyBxdWUgZXN0YW1vcyB0ZXN0ZWFuZG8gZXMgcXVlIGVsIHByb21lZGlvIGRlbCB2aWVudG8gZXN0ZSBkcmlzdHJpYnVpZG8gZGUgbWFuZXJhIGFsZWF0b3JpYSBzaWd1aWVuZG8gdW4gcHJvY2VzbyBhbGVhdG9yaW8uCmBgYHtyfQprbmVhIDwtIGtuZWFybmVpZ2goZGF0YV9hZ2dfc2YpCm5laWIgPC0ga25uMm5iKGtuZWEpCgpsaXN0dyA8LSBuYjJsaXN0dyhuZWliKQoKbW9yYW5fdGVzdCA8LSBtb3Jhbi50ZXN0KGRhdGFfYWdnX3NmJE1FQU5fVklFTlRPX0tNSCwgbGlzdHcpCm1vcmFuX3Rlc3QKZ2VhcnlfdGVzdCA8LSBnZWFyeS50ZXN0KGRhdGFfYWdnX3NmJE1FQU5fVklFTlRPX0tNSCwgbGlzdHcpCmdlYXJ5X3Rlc3QKCnNoYXBoaXJvX3Rlc3QgPC0gc2hhcGlyby50ZXN0KGRhdGFfYWdnX3NmJE1FQU5fVklFTlRPX0tNSCkKc2hhcGhpcm9fdGVzdApgYGAKCmBgYHtyfQptb3JhbiA8LSBtb3Jhbi5wbG90KGRhdGFfYWdnX3NmJE1FQU5fVklFTlRPX0tNSCwgbGlzdHcgPSBsaXN0dykKYGBgCgpgYGB7cn0KZGF0YV9hZ2dfc2ZbMTAsXQpkYXRhX2FnZ19zZlszMSxdCmRhdGFfYWdnX3NmWzc5LF0KZGF0YV9hZ2dfc2ZbMTA4LF0KZGF0YV9hZ2dfc2ZbNjgsXQpkYXRhX2FnZ19zZlsyMSxdCmRhdGFfYWdnX3NmWzg5LF0KZGF0YV9hZ2dfc2ZbMjMsXQpkYXRhX2FnZ19zZlsxMDksXQpgYGAKYGBge3J9CmRhdGFfYWdnX3NmID0gZGF0YV9hZ2dfc2ZbZGF0YV9hZ2dfc2YkTk9NQlJFICE9ICJDQVRBTUFSQ0EgQUVSTyIsXQpkYXRhX2FnZ19zZiA9IGRhdGFfYWdnX3NmW2RhdGFfYWdnX3NmJE5PTUJSRSAhPSAiR0VORVJBTCBQSUNPIEFFUk8iLF0KZGF0YV9hZ2dfc2YgPSBkYXRhX2FnZ19zZltkYXRhX2FnZ19zZiROT01CUkUgIT0gIlJJTyBDVUFSVE8gQUVSTyIsXQpkYXRhX2FnZ19zZiA9IGRhdGFfYWdnX3NmW2RhdGFfYWdnX3NmJE5PTUJSRSAhPSAiVkVOQURPIFRVRVJUTyBBRVJPIixdCmRhdGFfYWdnX3NmID0gZGF0YV9hZ2dfc2ZbZGF0YV9hZ2dfc2YkTk9NQlJFICE9ICJQSUdVRSBBRVJPIixdCmRhdGFfYWdnX3NmID0gZGF0YV9hZ2dfc2ZbZGF0YV9hZ2dfc2YkTk9NQlJFICE9ICJDT1JPTkVMIFNVQVJFWiBBRVJPIixdCmRhdGFfYWdnX3NmID0gZGF0YV9hZ2dfc2ZbZGF0YV9hZ2dfc2YkTk9NQlJFICE9ICJTQU4gTFVJUyBBRVJPIixdCmRhdGFfYWdnX3NmID0gZGF0YV9hZ2dfc2ZbZGF0YV9hZ2dfc2YkTk9NQlJFICE9ICJET0xPUkVTIEFFUk8iLF0KZGF0YV9hZ2dfc2YgPSBkYXRhX2FnZ19zZltkYXRhX2FnZ19zZiROT01CUkUgIT0gIlZJQ1RPUklDQSIsXQpyb3duYW1lcyhkYXRhX2FnZ19zZikgPC0gMTpucm93KGRhdGFfYWdnX3NmKQpgYGAKCiMgSW5saWVyczogdmVyaWZpY2Ftb3MgbG9zIHJlc3VsdGFkb3MgZHNwIGRlIGVsaW1pbmFyIGlubGllcnMKYGBge3J9CiMgQ3JlYW1vcyB1bmEgbGlzdGEgZGUgdmVjaW5vcwprbmVhIDwtIGtuZWFybmVpZ2goZGF0YV9hZ2dfc2YpCm5laWIgPC0ga25uMm5iKGtuZWEpCmxpc3R3IDwtIG5iMmxpc3R3KG5laWIpCgojIEhhY2Vtb3MgZWwgdGVzdCBkZSBtb3JhbiAKbW9yYW5fdGVzdF92MiA8LSBtb3Jhbi50ZXN0KGRhdGFfYWdnX3NmJE1FQU5fVklFTlRPX0tNSCwgbGlzdHcpCm1vcmFuX3Rlc3RfdjIKCmdlYXJ5X3Rlc3RfdjIgPC0gZ2VhcnkudGVzdChkYXRhX2FnZ19zZiRNRUFOX1ZJRU5UT19LTUgsIGxpc3R3KQpnZWFyeV90ZXN0X3YyCgpzaGFwaGlyb190ZXN0X3YyIDwtIHNoYXBpcm8udGVzdChkYXRhX2FnZ19zZiRNRUFOX1ZJRU5UT19LTUgpCnNoYXBoaXJvX3Rlc3RfdjIKYGBgCmBgYHtyfQptb3Jhbi5wbG90KGRhdGFfYWdnX3NmJE1FQU5fVklFTlRPX0tNSCwgbGlzdHcgPSBsaXN0dykKcXFub3JtKGRhdGFfYWdnX3NmJE1FQU5fVklFTlRPX0tNSCkKcXFsaW5lKGRhdGFfYWdnX3NmJE1FQU5fVklFTlRPX0tNSCwgY29sPTIpCmBgYApgYGB7cn0KZGF0YV9hZ2dfc2ZbNTYsXQpkYXRhX2FnZ19zZls5NixdCmBgYAoKYGBge3J9CmRhdGFfYWdnX3NmID0gZGF0YV9hZ2dfc2ZbZGF0YV9hZ2dfc2YkTk9NQlJFICE9ICJPQkVSQSIsXQpkYXRhX2FnZ19zZiA9IGRhdGFfYWdnX3NmW2RhdGFfYWdnX3NmJE5PTUJSRSAhPSAiVFJFTlFVRSBMQVVRVUVOIixdCnJvd25hbWVzKGRhdGFfYWdnX3NmKSA8LSAxOm5yb3coZGF0YV9hZ2dfc2YpCmBgYAoKYGBge3J9CiMgQ3JlYW1vcyB1bmEgbGlzdGEgZGUgdmVjaW5vcwprbmVhIDwtIGtuZWFybmVpZ2goZGF0YV9hZ2dfc2YpCm5laWIgPC0ga25uMm5iKGtuZWEpCmxpc3R3IDwtIG5iMmxpc3R3KG5laWIpCgojIEhhY2Vtb3MgZWwgdGVzdCBkZSBtb3JhbiAKbW9yYW5fdGVzdF92MiA8LSBtb3Jhbi50ZXN0KGRhdGFfYWdnX3NmJE1FQU5fVklFTlRPX0tNSCwgbGlzdHcpCm1vcmFuX3Rlc3RfdjIKCmdlYXJ5X3Rlc3RfdjIgPC0gZ2VhcnkudGVzdChkYXRhX2FnZ19zZiRNRUFOX1ZJRU5UT19LTUgsIGxpc3R3KQpnZWFyeV90ZXN0X3YyCgpzaGFwaGlyb190ZXN0X3YyIDwtIHNoYXBpcm8udGVzdChkYXRhX2FnZ19zZiRNRUFOX1ZJRU5UT19LTUgpCnNoYXBoaXJvX3Rlc3RfdjIKYGBgCmBgYHtyfQptb3Jhbi5wbG90KGRhdGFfYWdnX3NmJE1FQU5fVklFTlRPX0tNSCwgbGlzdHcgPSBsaXN0dykKcXFub3JtKGRhdGFfYWdnX3NmJE1FQU5fVklFTlRPX0tNSCkKcXFsaW5lKGRhdGFfYWdnX3NmJE1FQU5fVklFTlRPX0tNSCwgY29sPTIpCmBgYAoKYGBge3J9CiMgQ0FMQ1VMQU1PUyBFTCBDT0VGSUNJRU5URSBERSBNT1JBTiBZIEVMIERFIEdFQVJZCm1vcmFuKGRhdGFfYWdnX3NmJE1FQU5fVklFTlRPX0tNSCwgbGlzdHcsIGxlbmd0aChsaXN0dyR3ZWlnaHRzKSxTemVybyhsaXN0dyksemVyby5wb2xpY3kgPSBGQUxTRSkKYGBgCiNUT0RPOiBBZ3JlZ2FyIGNvbmNsdXNpb24gYWNhCmBgYHtyfQpwbG90KGRhdGFfYWdnX3NmJE1FQU5fVklFTlRPX0tNSCkKYGBgCiMgVmFyaW9ncmFtYQpgYGB7cn0KdiA8LSB2YXJpb2dyYW0oTUVBTl9WSUVOVE9fS01IfjEsIGRhdGFfYWdnX3NmKQpwbG90KHYpCmBgYApgYGB7cn0KdnRfZXhwID0gZml0LnZhcmlvZ3JhbSh2LCB2Z20oMTI1LCAiRXhwIiwgMzAsIDUpKQp2dF9leHAKcGxvdCh2ICwgdnRfZXhwKQpgYGAKYGBge3J9CnZ0X21hdCA9IGZpdC52YXJpb2dyYW0odiwgdmdtKDEyNSwgIk1hdCIsIDMwLCA1KSkKcGxvdCh2ICwgdnRfbWF0KQpgYGAKYGBge3J9CnZ0X2V4YyA9IGZpdC52YXJpb2dyYW0odiwgdmdtKDEyNSwgIkV4YyIsIDMwLCA1KSkKcGxvdCh2ICwgdnRfZXhjKQpgYGAKYGBge3J9CnZ0X2JlcyA9IGZpdC52YXJpb2dyYW0odiwgdmdtKDEyNSwgIkJlcyIsIDMwLCA1KSkKcGxvdCh2ICwgdnRfYmVzKQpgYGAKYGBge3J9CmF0dHIodnRfZXhwLCAnU1NFcnInKQphdHRyKHZ0X21hdCwgJ1NTRXJyJykKYXR0cih2dF9leGMsICdTU0VycicpCmF0dHIodnRfYmVzLCAnU1NFcnInKQpgYGAKCktyaWdpbmcKCmBgYHtyfQpkZXBhcnRhbWVudG9zIDwtIHN0X3JlYWQoImRhdGFfZGVwYXJ0YW1lbnRvcy9Db2RnZW9fUGFpc194X2RwdG9fY29uX2RhdG9zL3B4ZHB0b2RhdG9zb2suc2hwIikKZGVwYXJ0YW1lbnRvcyA8LWRlcGFydGFtZW50b3NbZGVwYXJ0YW1lbnRvcyRkZXBhcnRhbWVuICE9ICJBbnTDoXJ0aWRhIEFyZ2VudGluYSIsXQpkZXBhcnRhbWVudG9zIDwtZGVwYXJ0YW1lbnRvc1tkZXBhcnRhbWVudG9zJGRlcGFydGFtZW4gIT0gIklzbGFzIGRlbCBBdGzDoW50aWNvIFN1ciIsXQpkZXBhcnRhbWVudG9zIDwtIGFzX1NwYXRpYWwoZGVwYXJ0YW1lbnRvcykKZ3JpbGxhIDwtIGFzLmRhdGEuZnJhbWUoc3BzYW1wbGUoZGVwYXJ0YW1lbnRvcywgdHlwZT0icmVndWxhciIsIG49NTAwMCkpCm5hbWVzKGdyaWxsYSkgPC0gYygiWCIsICJZIikKY29vcmRpbmF0ZXMoZ3JpbGxhKSA8LSBjKCJYIiwgIlkiKQpwbG90KGdyaWxsYSkKZ3JpZGRlZChncmlsbGEpIDwtIFRSVUUKZnVsbGdyaWQoZ3JpbGxhKSA8LSBUUlVFCnBsb3QoZ3JpbGxhKQpwcm9qNHN0cmluZyhncmlsbGEpIDwtIHByb2o0c3RyaW5nKGRlcGFydGFtZW50b3MpCmRhdGFfYWdnX3NmIDwtIGFzX1NwYXRpYWwoZGF0YV9hZ2dfc2YpCnByb2o0c3RyaW5nKGRhdGFfYWdnX3NmKSA8LSBwcm9qNHN0cmluZyhkZXBhcnRhbWVudG9zKQpgYGAKYGBge3J9CmtvMSA8LSBrcmlnZShNRUFOX1ZJRU5UT19LTUh+MSwgZGF0YV9hZ2dfc2YsIGdyaWxsYSwgbW9kZWwgPSB2dF9leHAsIG5tYXg9MjApCmBgYApgYGB7cn0Kc3BwbG90KGtvMVsidmFyMS5wcmVkIl0pCnNwcGxvdChrbzFbInZhcjEudmFyIl0pCmBgYApgYGB7cn0Ka28yIDwtIGtyaWdlKE1FQU5fVklFTlRPX0tNSH4xLCBkYXRhX2FnZ19zZiwgZ3JpbGxhLCBtb2RlbCA9IHZ0X2V4cCwgbm1heD01MCkKc3BwbG90KGtvMlsidmFyMS5wcmVkIl0pCnNwcGxvdChrbzJbInZhcjEudmFyIl0pCmBgYApgYGB7cn0Ka28zIDwtIGtyaWdlKE1FQU5fVklFTlRPX0tNSH4xLCBkYXRhX2FnZ19zZiwgZ3JpbGxhLCBtb2RlbCA9IHZ0X2Jlcywgbm1heD01MCkKc3BwbG90KGtvM1sidmFyMS5wcmVkIl0pCnNwcGxvdChrbzNbInZhcjEudmFyIl0pCmBgYAoKYGBge3J9CmxpYnJhcnkocmFzdGVyKQpyIDwtIHJhc3RlcihrbzEpCnIubSA8LSBtYXNrKHIsIGRlcGFydGFtZW50b3MpCmBgYAoKYGBge3J9CmxpYnJhcnkodG1hcCkKdG1fc2hhcGUoci5tKSArCiAgdG1fcmFzdGVyKG49MTAsIAogICAgICAgICAgICBwYWxldHRlPSJCbHVlcyIsCiAgICAgICAgICAgIGF1dG8ucGFsZXR0ZS5tYXBwaW5nPUZBTFNFLAp0aXRsZT0iIikgKwp0bV9sZWdlbmQobGVnZW5kLm91dHNpZGU9VFJVRSkKYGBgCmBgYHtyfQpyIDwtIHJhc3RlcihrbzEsIGxheWVyPSJ2YXIxLnZhciIpCnIubSA8LSBtYXNrKHIsIGRlcGFydGFtZW50b3MpCgp0bV9zaGFwZShyLm0pICsKdG1fcmFzdGVyKG49NywgCiAgICAgICAgICBwYWxldHRlID0iUmVkcyIsCiAgICAgICAgICB0aXRsZT0iVmFyaWFuY2UgbWFwICIpICsKdG1fbGVnZW5kKGxlZ2VuZC5vdXRzaWRlPVRSVUUpCmBgYApgYGB7cn0KciA8LSBzcXJ0KHJhc3RlcihrbzEsIGxheWVyPSJ2YXIxLnZhciIpKSAqIDEuOTYKci5tIDwtIG1hc2sociwgZGVwYXJ0YW1lbnRvcykKCnRtX3NoYXBlKHIubSkgKwp0bV9yYXN0ZXIobj03LCAKICAgICAgICAgIHBhbGV0dGUgPSJSZWRzIiwKICAgICAgICAgIHRpdGxlPSI5NSUgQ0kgbWFwIFxuKGVuIGttL2gpIikgKwp0bV9sZWdlbmQobGVnZW5kLm91dHNpZGU9VFJVRSkKYGBgCgoKCg==